#
#   api_lastfm.py
#
#   David Janes
#   2008.11.22
#
#	Copyright 2008, 2009 David Janes
#
#	Interface to Last.FM
#

import os
import sys
import urllib
import types
import pprint
import types
import re

import bm_extract
import bm_uri
import api
import bm_cfg

import hcard
import vcard
import vcalendar
import mfdict

user_re = '^http://www[.]last[.]fm/user/(?P<userid>[^/]+)$'
user_rex = re.compile(user_re)

class LastFM(api.APIReader):
	_base_uri = "http://ws.audioscrobbler.com/2.0/"
	_atom_item = {
		"title" : "name", 
		"id" : "mbid", 
		"link" : "url", 
		"content" : "description", 
	}
	_is_organization = False
	_is_person = False

	def __init__(self, **ad):
		ad["uri"] = "http://ws.audioscrobbler.com/2.0/"

		api.APIReader.__init__(self, **ad)

	def CustomizeAtomItem(self, d):
		d = api.APIReader.CustomizeAtomItem(self, d)

		#
		#	Well-known last.fm data issue
		#
		link = bm_extract.as_string(d, "link")
		if bm_uri.scheme(link) == None:
			d["link"] = "http://%s" % link

		return	d

	def _ExtractArtistAsAuthor(self, d):
		author = {}

		value = bm_extract.as_string(d, "artist.url")
		if value:
			author["uri"] = value

		value = bm_extract.as_string(d, "artist.name")
		if value:
			author["@"] = value

		if author:
			d['author'] = author

		api.remove_keys(d, [ "artist", ])
		
	def _ExtractCreatorAsAuthor(self, d):
		try:
			value = d.pop("creator")
			if not value:
				return
				
			authord = {
				"uri" : value,
			}

			user_match = user_rex.match(value)
			if user_match:
				authord["@"] = user_match.group('userid')
			else:
				authord["@"] = bm_uri.trim(value)

			d['author'] = authord
		except KeyError:
			pass
		
	def _ExtractDate(self, d):
		try:
			date = d.pop('date')
			if date:
				date = bm_extract.coerce_datetime(date, otherwise = None, atom = True)
				if date:
					d['updated'] = date
		except KeyError:
			pass

	def _ExtractBio(self, d):
		content = bm_extract.as_string(d, "bio.content")
		if content and not d.get('content'):
			d["content"] = content

		api.remove_keys(d, [ "bio", ])

	def _ExtractImages(self, d):
		for image_url in bm_extract.as_list(d, "image"):
			api.add_media(d, url = image_url)

		api.remove_keys(d, [ "image", ])

	def _ExtractVenue(self, d):
		if d.has_key("venue"):
			mfd = mfdict.mfdict()

			for k_to, k_from in [
				( vcard.OrganizationName, "venue.name", ),
				( vcard.URL, "venue.url", ),
				( vcard.TZ, "venue.location.timezone", ),
				( vcard.Locality, "venue.location.city", ),
				( vcard.CountryName, "venue.location.country", ),
				( vcard.Latitude, "venue.location.point.lat", ),
				( vcard.Longitude, "venue.location.point.long", ),
			]:
				value = bm_extract.as_string(d, k_from)
				if value:
					mfd[k_to] = value

			try:
				id = d.pop("tag")
				if id:
					mfd[vcard.UID] = bm_extract.coerce_string(id)
			except KeyError:
				pass

			vcard.scrub(mfd)
			d["hcard:hcard"] = vcard.decompose(mfd)

			#
			#	top level geocode
			#
			api.add_latlon(d, mfd.find("latitude"), mfd.find("longitude"))

		api.remove_keys(d, [ "venue", ])

	def _ExtractEvent(self, d):
		dtstart = bm_extract.as_datetime(d, "startDate", otherwise = None, atom = True)
		if dtstart:
			eventd = {
				vcalendar.DTStart : dtstart
			}

			dtend = bm_extract.as_datetime(d, "endDate", otherwise = None, atom = True)
			if dtend:
				eventd[vcalendar.DTEnd] = dtend

			d["hcalendar:hcalendar"] = eventd

		api.remove_keys(d, [ "startDate", "endDate", ])

	def _Cleanup(self, d, key = "lastfm:lasfm"):
		api.separate_atom_items(d, key)

		if self._is_organization:
			self._Organization(d)
		elif self._is_person:
			self._Person(d)

		link = bm_extract.as_string(d, "link")
		user_match = user_rex.match(link)
		if user_match:
			d['sgn:ident'] = "sgn://last.fm/%s" % user_match.group('userid')

	def _ExtractTitleAsTag(self, d):
		title = d.get("title")
		if title:
			d["category"] = [
				{
					"term" : title
				}
			]

	def _Organization(self, d):
		mfd = mfdict.mfdict()
		mfd[vcard.OrganizationName] = d.get("title", "")
		mfd[vcard.URL] = d.get("link", "")

		values = bm_extract.as_list(d, "media:group//@url")
		if values:
			mfd[vcard.Photo] = values[-1]

		vcard.scrub(mfd)

		d["hcard:hcard"] = vcard.decompose(mfd)

	def _Person(self, d):
		mfd = mfdict.mfdict()
		mfd[vcard.FN] = d.get("title", "")
		mfd[vcard.URL] = d.get("link", "")

		values = bm_extract.as_list(d, "media:group//@url")
		if values:
			mfd[vcard.Photo] = values[-1]

		vcard.scrub(mfd)

		d["hcard:hcard"] = vcard.decompose(mfd)

class Artist(LastFM):
	"""
	See: http://www.last.fm/api/show?service=272
	"""

	def __init__(self, **ad):
		LastFM.__init__(self, **ad)

	def CustomizeRestart(self):
		if not self._request_meta.get('api_key'):
			self._request_meta['api_key'] = bm_cfg.cfg.as_string('lastfm.api_key')

	def CustomizePageURI(self, page_index):
		if page_index > 1:
			return	"page=%s" % page_index

	def CustomizeAtomItem(self, d):
		d = LastFM.CustomizeAtomItem(self, d)

		self._ExtractArtistAsAuthor(d)
		self._ExtractImages(d)
		self._ExtractBio(d)
		self._ExtractVenue(d)
		self._ExtractEvent(d)
		self._Cleanup(d)

		return	d

class ArtistEvents(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "events.event"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.getevents",
	}

class ArtistInfo(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "artist"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.getinfo",
	}
	_is_organization = True

class ArtistSimilar(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "similarartists.artist"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.getsimilar",
	}
	_is_organization = True

class ArtistTopAlbums(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "topalbums.album"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.gettopalbums",
	}

class ArtistTopFans(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "topfans.user"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.gettopfans",
	}

class ArtistTopTags(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "toptags.tag"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.gettoptags",
	}

	def CustomizeAtomItem(self, d):
		d = Artist.CustomizeAtomItem(self, d)

		self._ExtractTitleAsTag(d)

		return	d

class ArtistTopTracks(Artist):	## validated
	_page_max = -1
	_page_max_path = None
	_item_path = "toptracks.track"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.gettoptracks",
	}

class ArtistSearch(Artist):		## XXX
	_page_max = -1
	_page_max_path = "results.totalResults"
	_item_path = "artist"
	_required_attributes = [ "artist", ]
	_base_query = {
		"method" : "artist.search",
	}

class User(LastFM):
	"""
	See: http://www.last.fm/api/show?service=272
	"""

	_required_attributes = [ "user", ]

	def __init__(self, **ad):
		LastFM.__init__(self, **ad)

	def CustomizeRestart(self):
		if not self._request_meta.get('api_key'):
			self._request_meta['api_key'] = bm_cfg.cfg.as_string('lastfm.api_key')

	def CustomizePageURI(self, page_index):
		if page_index > 1:
			return	"page=%s" % page_index

	def CustomizeAtomItem(self, d):
		d = LastFM.CustomizeAtomItem(self, d)

		self._ExtractArtistAsAuthor(d)
		self._ExtractImages(d)
		self._ExtractBio(d)
		self._ExtractVenue(d)
		self._ExtractEvent(d)
		self._ExtractDate(d)
		self._Cleanup(d)

		return	d

class UserLovedTracks(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "lovedtracks.track"
	_base_query = {
		"method" : "user.getLovedTracks",
	}

class UserEvents(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "events.event"
	_base_query = {
		"method" : "user.getEvents",
	}

class UserFriends(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "friends.user"
	_base_query = {
		"method" : "user.getFriends",
	}
	_is_person = True

	def CustomizeAtomItem(self, d):
		try:
			realname = d.pop('realname')
		except:
			realname = None

		d = User.CustomizeAtomItem(self, d)

		if realname:
			d['title'] = realname

		return	d

class UserNeighbours(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "neighbours.user"
	_base_query = {
		"method" : "user.getNeighbours",
	}
	_is_person = True

	def CustomizeAtomItem(self, d):
		d = User.CustomizeAtomItem(self, d)

		try:
			realname = d.pop('realname')
			if realname:
				d['title'] = realname
		except:
			pass

		return	d

class UserPastEvents(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "events.event"
	_base_query = {
		"method" : "user.getPastEvents",
	}

class UserPlaylists(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "playlists.playlist"
	_base_query = {
		"method" : "user.getPlaylists",
	}

	def CustomizeAtomItem(self, d):
		d = LastFM.CustomizeAtomItem(self, d)

		self._ExtractImages(d)
		self._ExtractCreatorAsAuthor(d)
		self._Cleanup(d, "lastfm:playlist")

		return	d

class UserRecentTracks(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "recenttracks.track"
	_base_query = {
		"method" : "user.getRecentTracks",
	}

class UserRecommendedArtists(User):	## NOT WORKING
	_page_max = -1
	_page_max_path = None
	_item_path = "events.event"
	_base_query = {
		"method" : "user.getRecommendedArtists",
	}

class UserRecommendedEvents(User):	## NOT WORKING
	_page_max = -1
	_page_max_path = None
	_item_path = "events.event"
	_base_query = {
		"method" : "user.getRecommendedEvents",
	}

class UserShouts(User):	## NOTWORKING
	_page_max = -1
	_page_max_path = None
	_item_path = "shouts.shout"
	_base_query = {
		"method" : "user.getShouts",
	}

class UserTopAlbums(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "topalbums.album"
	_base_query = {
		"method" : "user.getTopAlbums",
	}

class UserTopArtists(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "topartists.artist"
	_base_query = {
		"method" : "user.getTopArtists",
	}
	_is_organization = True

class UserTopTags(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "toptags.tag"
	_base_query = {
		"method" : "user.getTopTags",
	}

	def CustomizeAtomItem(self, d):
		d = User.CustomizeAtomItem(self, d)

		self._ExtractTitleAsTag(d)

		return	d

class UserTopTracks(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "toptracks.track"
	_base_query = {
		"method" : "user.getTopTracks",
	}

class UserWeeklyAlbumChart(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "weeklyalbumchart.album"
	_base_query = {
		"method" : "user.getWeeklyAlbumChart",
	}

class UserWeeklyArtistChart(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "weeklyartistchart.artist"
	_base_query = {
		"method" : "user.getWeeklyArtistChart",
	}

class UserWeeklyChartList(User):	## NOT WORKING
	_page_max = -1
	_page_max_path = None
	_item_path = "events.event"
	_base_query = {
		"method" : "user.getWeeklyChartList",
	}

class UserWeeklyTrackChart(User):
	_page_max = -1
	_page_max_path = None
	_item_path = "weeklytrackchart.track"
	_base_query = {
		"method" : "user.getWeeklyTrackChart",
	}

if __name__ == '__main__':
	bm_cfg.cfg.initialize()

	from bm_log import Log
	Log.verbose = True

	try:
		import json
	except:
		import simplejson as json

	action = "artist.getInfo"
	if len(sys.argv) > 1:
		action = sys.argv[1]

	query = 'Killers'
	action = UserLovedTracks

	apio = None
	apio = ArtistEvents(artist = query)
	## apio = ArtistInfo(artist = query)
	## apio = ArtistSimilar(artist = query)
	## apio = ArtistTopAlbums(artist = query)
	## apio = ArtistTopFans(artist = query)
	## apio = ArtistTopTags(artist = query)
	## apio = ArtistTopTracks(artist = query)
	## apio = ArtistSearch(artist = query)
	## apio = UserLovedTracks(user = 'dpjanes')
	## apio = UserEvents(user = 'mokele')
	## apio = UserFriends(user = 'dpjanes')
	## apio = UserNeighbours(user = 'dpjanes')
	## apio = UserPastEvents(user = 'mokele')
	## apio = UserPlaylists(user = 'mokele')
	## apio = UserRecentTracks(user = 'dpjanes')
	## apio = UserRecommendedArtists(user = 'dpjanes')
	## apio = UserRecommendedEvents(user = 'dpjanes')
	## apio = UserShouts(user = 'dpjanes')
	## apio = UserTopAlbums(user = 'dpjanes')
	## apio = UserTopArtists(user = 'dpjanes')
	## apio = UserTopTags(user = 'dpjanes')
	## apio = UserTopTracks(user = 'dpjanes')
	## apio = UserWeeklyAlbumChart(user = 'dpjanes')
	## apio = UserWeeklyArtistChart(user = 'dpjanes')
	## apio = UserWeeklyChartList(user = 'smack416')
	## apio = UserWeeklyTrackChart(user = 'dpjanes')

	if apio:
		any = False

		for item in apio.items:
			any = True
			pprint.pprint(item)

		if not any:
			pprint.pprint(apio.response)

	else:
		print >> sys.stderr, "no query?"
